<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>开始使用GTK+</title>
<meta name="generator" content="DocBook XSL Stylesheets V1.76.1">
<link rel="home" href="index.html" title="GTK+ 3 参考手册">
<link rel="up" href="gtk.html" title="Part I. GTK+概述">
<link rel="prev" href="gtk.html" title="Part I. GTK+概述">
<link rel="next" href="gtk-building.html" title="Compiling the GTK+ libraries">
<meta name="generator" content="GTK-Doc V1.18 (XML mode)">
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<table class="navigation" id="top" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2"><tr valign="middle">
<td><a accesskey="p" href="gtk.html"><img src="left.png" width="24" height="24" border="0" alt="Prev"></a></td>
<td><a accesskey="u" href="gtk.html"><img src="up.png" width="24" height="24" border="0" alt="Up"></a></td>
<td><a accesskey="h" href="index.html"><img src="home.png" width="24" height="24" border="0" alt="Home"></a></td>
<th width="100%" align="center">GTK+ 3 参考手册</th>
<td><a accesskey="n" href="gtk-building.html"><img src="right.png" width="24" height="24" border="0" alt="Next"></a></td>
</tr></table>
<div class="chapter">
<div class="titlepage"><div><div><h2 class="title">
<a name="gtk-getting-started"></a>开始使用GTK+</h2></div></div></div>
<p>这个文章中包含一个GTK+编程的入门教程，这里有个前提是你已经把GTK+环境配置好了，如已经准备好它的依赖库 和 C 编译器.如果你想先编译gtk+,可以参考 <a class="link" href="gtk-compiling.html" title="编译 GTK+ 程序">编译 GTK+ 库</a>相关章节。</p>
<div class="simplesect">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="id3116225"></a>基础</h2></div></div></div>
<p>现在开始介绍GTK ，我们先以一个简单的程序开始，这个程序将建立一个内容为空 200*200像素的窗口:</p>
<p>
      <img src="window-default.png">
    </p>
<div class="informalexample"><pre class="programlisting">
      #include &lt;gtk/gtk.h&gt;

int
main (int   argc,
      char *argv[])
{
  GtkWidget *window;

  gtk_init (&amp;argc, &amp;argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);

  gtk_widget_show (window);

  gtk_main ();

  return 0;
}

    </pre></div>
<p>你可以由以下的gcc命令编译这个程序:</p>
<div class="literallayout"><p><br>
      <code class="literal">gcc `pkg-config --cflags gtk+-3.0` -o window-default window-default.c `pkg-config --libs gtk+-3.0`</code><br>
    </p></div>
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
<h3 class="title">Note</h3>
<p>关于更多编译一个gtk+应用程序的信息，请引用<a class="link" href="gtk-compiling.html" title="编译 GTK+ 程序">编译GTK+ 应用程序</a>。</p>
</div>
<p>任何的gtk+版本都是可以的。包含<code class="filename">gtk/gtk.h</code>, 里面声明了gtk+应用程序需要的函数声明和类型还有宏。</p>
<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
<h3 class="title">Warning</h3>
<p>可能gtk+的安装需要多个头文件，但仅需要包含顶层头<code class="filename">gtk/gtk.h</code>到第三方代码中就可以了，当直接包含了其他的头，编译器会中止和出现错误信息。</p>
</div>
<p>我们在<code class="function">main</code>()函数中处理这个程序，定义一个 #GtkWidget 类型指针的<code class="varname">window</code>变量。</p>
<p>接着的一行，我们将调用GTK+的初始化函数gtk_init()；这个函数将会为GTK+建立起类型系统，到窗体环境的连接等等。gtk_init()将由命令行所传递的参数作为自己的参数，这样就可由GTK+来解析一些特殊的命令行参数以控制GTK+自己的行为。被解析的参数将会从字符串中被删除，只留下那些GTK+无法识别的参数以供应用程序去解析。</p>
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
<h3 class="title">Note</h3>
<p>若想获得更多关于GTK+可识别的参数的信息，请参考本文档中<a class="link" href="gtk-running.html" title="运行 GTK+ 应用程序">运行GTK+应用程序</a> 一章。</p>
</div>
<p>The call to gtk_window_new() will create a new #GtkWindow and store
    it inside the <code class="varname">window</code> variable. The type of the window
    is %GTK_WINDOW_TOPLEVEL, which means that the #GtkWindow will be managed
    by the windowing system: it will have a frame, a title bar and window
    controls, depending on the platform.</p>
<p>In order to terminate the application when the #GtkWindow is
    destroyed, we connect the #GtkWidget::destroy signal to the gtk_main_quit()
    function. This function will terminate the GTK+ main loop started by calling
    gtk_main() later. The #GtkWidget::destroy signal is emitted when a widget is
    destroyed, either by explicitly calling gtk_widget_destroy() or when the
    widget is unparented. Top-level #GtkWindows are also destroyed when
    the Close window control button is clicked.</p>
<p>#GtkWidgets are hidden by default. By calling gtk_widget_show()
    on a #GtkWidget we are asking GTK+ to set the visibility attribute so that it
    can be displayed. All this work is done after the main loop has been
    started.</p>
<p>The last line of interest is the call to gtk_main(). This function will
    start the GTK+ main loop and will block the control flow of the
    main() until the gtk_main_quit() function is called.</p>
<p>While the program is running, GTK+ is receiving
    <em class="firstterm">events</em>. These are typically input events caused by
    the user interacting with your program, but also things like messages from
    the window manager or other applications. GTK+ processes these and as a
    result, <em class="firstterm">signals</em> may be emitted on your widgets.
    Connecting handlers for these signals is how you normally make your
    program do something in response to user input.</p>
<p>The following example is slightly more complex, and tries to
    showcase some of the capabilities of GTK+.</p>
<p>编程传统<span class="emphasis"><em>Hello, World</em></span>.</p>
<p>
      <img src="hello-world.png">
    </p>
<div class="example">
<a name="gtk-getting-started-hello-world"></a><p class="title"><b>Example 1. Hello World （GTK+ 版）</b></p>
<div class="example-contents"><pre class="programlisting">
        #include &lt;gtk/gtk.h&gt;

/* This is a callback function. The data arguments are ignored
 * in this example. More on callbacks below.
 */
static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

static gboolean
on_delete_event (GtkWidget *widget,
                 GdkEvent  *event,
                 gpointer   data)
{
  /* If you return FALSE in the "delete_event" signal handler,
   * GTK will emit the "destroy" signal. Returning TRUE means
   * you don't want the window to be destroyed.
   *
   * This is useful for popping up 'are you sure you want to quit?'
   * type dialogs.
   */

  g_print ("delete event occurred\n");

  return TRUE;
}

int
main (int   argc,
      char *argv[])
{
  /* GtkWidget is the storage type for widgets */
  GtkWidget *window;
  GtkWidget *button;

  /* This is called in all GTK applications. Arguments are parsed
   * from the command line and are returned to the application.
   */
  gtk_init (&amp;argc, &amp;argv);

  /* create a new window, and set its title */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Hello");

  /* When the window emits the "delete-event" signal (which is emitted
   * by GTK+ in response to an event coming from the window manager,
   * usually as a result of clicking the "close" window control), we
   * ask it to call the on_delete_event() function as defined above.
   *
   * The data passed to the callback function is NULL and is ignored
   * in the callback function.
   */
  g_signal_connect (window, "delete-event", G_CALLBACK (on_delete_event), NULL);

  /* Here we connect the "destroy" event to the gtk_main_quit() function.
   *
   * This signal is emitted when we call gtk_widget_destroy() on the window,
   * or if we return FALSE in the "delete_event" callback.
   */
  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);

  /* Sets the border width of the window. */
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  /* Creates a new button with the label "Hello World". */
  button = gtk_button_new_with_label ("Hello World");

  /* When the button receives the "clicked" signal, it will call the
   * function print_hello() passing it NULL as its argument.
   *
   * The print_hello() function is defined above.
   */
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);

  /* The g_signal_connect_swapped() function will connect the "clicked" signal
   * of the button to the gtk_widget_destroy() function; instead of calling it
   * using the button as its argument, it will swap it with the user data
   * argument. This will cause the window to be destroyed by calling
   * gtk_widget_destroy() on the window.
   */
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);

  /* This packs the button into the window. A GtkWindow inherits from GtkBin,
   * which is a special container that can only have one child
   */
  gtk_container_add (GTK_CONTAINER (window), button);

  /* The final step is to display this newly created widget... */
  gtk_widget_show (button);

  /* ... and the window */
  gtk_widget_show (window);

  /* All GTK applications must have a gtk_main(). Control ends here
   * and waits for an event to occur (like a key press or a mouse event),
   * until gtk_main_quit() is called.
   */
  gtk_main ();

  return 0;
}

      </pre></div>
</div>
<br class="example-break">
</div>
<div class="simplesect">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="id3126492"></a>Packing</h2></div></div></div>
<p>When creating an application, you'll want to put more than one widget
    inside a window. Our first helloworld example only used one widget so we
    could simply use a gtk_container_add() call to "pack" the widget into the
    window. But when you want to put more than one widget into a window, it
    it becomes important to control how each widget is positioned and sized.
    This is where packing comes in.</p>
<p>GTK+ comes with a large variety of <em class="firstterm">layout containers</em>
    whose purpose it is to control the layout of the child widgets that are
    added to them. See <a class="xref" href="LayoutContainers.html" title="布局容器"><i>布局容器</i></a> for an overview.</p>
<p>下面的例子中，将展示怎样用 GtkGrid 容器布置按钮：</p>
<p>
      <img src="grid-packing.png">
    </p>
<div class="example">
<a name="gtk-getting-started-grid-packing"></a><p class="title"><b>Example 2. Packing buttons</b></p>
<div class="example-contents"><pre class="programlisting">
        #include &lt;gtk/gtk.h&gt;

static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

int
main (int   argc,
      char *argv[])
{
  GtkWidget *window;
  GtkWidget *grid;
  GtkWidget *button;

  /* This is called in all GTK applications. Arguments are parsed
   * from the command line and are returned to the application.
   */
  gtk_init (&amp;argc, &amp;argv);

  /* create a new window, and set its title */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Grid");
  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  /* Here we construct the container that is going pack our buttons */
  grid = gtk_grid_new ();

  /* Pack the container in the window */
  gtk_container_add (GTK_CONTAINER (window), grid);

  button = gtk_button_new_with_label ("Button 1");
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);

  /* Place the first button in the grid cell (0, 0), and make it fill
   * just 1 cell horizontally and vertically (ie no spanning)
   */
  gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1);

  button = gtk_button_new_with_label ("Button 2");
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);

  /* Place the second button in the grid cell (1, 0), and make it fill
   * just 1 cell horizontally and vertically (ie no spanning)
   */
  gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1);

  button = gtk_button_new_with_label ("Quit");
  g_signal_connect (button, "clicked", G_CALLBACK (gtk_main_quit), NULL);

  /* Place the Quit button in the grid cell (0, 1), and make it
   * span 2 columns.
   */
  gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1);

  /* Now that we are done packing our widgets, we show them all
   * in one go, by calling gtk_widget_show_all() on the window.
   * This call recursively calls gtk_widget_show() on all widgets
   * that are contained in the window, directly or indirectly.
   */
  gtk_widget_show_all (window);

  /* All GTK applications must have a gtk_main(). Control ends here
   * and waits for an event to occur (like a key press or a mouse event),
   * until gtk_main_quit() is called.
   */
  gtk_main ();

  return 0;
}

      </pre></div>
</div>
<br class="example-break">
</div>
<div class="simplesect">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="id3119727"></a>Drawing</h2></div></div></div>
<p>Many widgets, like buttons, do all their drawing themselves. You
    just tell them the label you want to see, and they figure out what font
    to use, draw the button outline and focus rectangle, etc. Sometimes, it
    is necessary to do some custom drawing. In that case, a #GtkDrawingArea
    might be the right widget to use. It offers a canvas on which you can
    draw by connecting to the #GtkWidget::draw signal.
    </p>
<p>The contents of a widget often need to be partially or fully redrawn,
    e.g. when another window is moved and uncovers part of the widget, or
    when tie window containing it is resized. It is also possible to explicitly
    cause part or all of the widget to be redrawn, by calling
    gtk_widget_queue_draw() or its variants. GTK+ takes care of most of the
    details by providing a ready-to-use cairo context to the ::draw signal
    handler.</p>
<p>The following example shows a ::draw signal handler. It is a bit
    more complicated than the previous examples, since it also demonstrates
    input event handling by means of ::button-press and ::motion-notify
    handlers.</p>
<p>
      <img src="drawing.png">
    </p>
<div class="example">
<a name="gtk-getting-started-drawing"></a><p class="title"><b>Example 3. Drawing in response to input</b></p>
<div class="example-contents"><pre class="programlisting">
        #include &lt;gtk/gtk.h&gt;

/* Surface to store current scribbles */
static cairo_surface_t *surface = NULL;

static void
clear_surface (void)
{
  cairo_t *cr;

  cr = cairo_create (surface);

  cairo_set_source_rgb (cr, 1, 1, 1);
  cairo_paint (cr);

  cairo_destroy (cr);
}

/* Create a new surface of the appropriate size to store our scribbles */
static gboolean
configure_event_cb (GtkWidget         *widget,
                    GdkEventConfigure *event,
                    gpointer           data)
{
  if (surface)
    cairo_surface_destroy (surface);

  surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
                                               CAIRO_CONTENT_COLOR,
                                               gtk_widget_get_allocated_width (widget),
                                               gtk_widget_get_allocated_height (widget));

  /* Initialize the surface to white */
  clear_surface ();

  /* We've handled the configure event, no need for further processing. */
  return TRUE;
}

/* Redraw the screen from the surface. Note that the ::draw
 * signal receives a ready-to-be-used cairo_t that is already
 * clipped to only draw the exposed areas of the widget
 */
static gboolean
draw_cb (GtkWidget *widget,
         cairo_t   *cr,
         gpointer   data)
{
  cairo_set_source_surface (cr, surface, 0, 0);
  cairo_paint (cr);

  return FALSE;
}

/* Draw a rectangle on the surface at the given position */
static void
draw_brush (GtkWidget *widget,
            gdouble    x,
            gdouble    y)
{
  cairo_t *cr;

  /* Paint to the surface, where we store our state */
  cr = cairo_create (surface);

  cairo_rectangle (cr, x - 3, y - 3, 6, 6);
  cairo_fill (cr);

  cairo_destroy (cr);

  /* Now invalidate the affected region of the drawing area. */
  gtk_widget_queue_draw_area (widget, x - 3, y - 3, 6, 6);
}

/* Handle button press events by either drawing a rectangle
 * or clearing the surface, depending on which button was pressed.
 * The ::button-press signal handler receives a GdkEventButton
 * struct which contains this information.
 */
static gboolean
button_press_event_cb (GtkWidget      *widget,
                       GdkEventButton *event,
                       gpointer        data)
{
  /* paranoia check, in case we haven't gotten a configure event */
  if (surface == NULL)
    return FALSE;

  if (event-&gt;button == 1)
    {
      draw_brush (widget, event-&gt;x, event-&gt;y);
    }
  else if (event-&gt;button == 3)
    {
      clear_surface ();
      gtk_widget_queue_draw (widget);
    }

  /* We've handled the event, stop processing */
  return TRUE;
}

/* Handle motion events by continuing to draw if button 1 is
 * still held down. The ::motion-notify signal handler receives
 * a GdkEventMotion struct which contains this information.
 */
static gboolean
motion_notify_event_cb (GtkWidget      *widget,
                        GdkEventMotion *event,
                        gpointer        data)
{
  int x, y;
  GdkModifierType state;

  /* paranoia check, in case we haven't gotten a configure event */
  if (surface == NULL)
    return FALSE;

  /* This call is very important; it requests the next motion event.
   * If you don't call gdk_window_get_pointer() you'll only get
   * a single motion event. The reason is that we specified
   * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
   * If we hadn't specified that, we could just use event-&gt;x, event-&gt;y
   * as the pointer location. But we'd also get deluged in events.
   * By requesting the next event as we handle the current one,
   * we avoid getting a huge number of events faster than we
   * can cope.
   */
  gdk_window_get_pointer (event-&gt;window, &amp;x, &amp;y, &amp;state);

  if (state &amp; GDK_BUTTON1_MASK)
    draw_brush (widget, x, y);

  /* We've handled it, stop processing */
  return TRUE;
}

static void
close_window (void)
{
  if (surface)
    cairo_surface_destroy (surface);

  gtk_main_quit ();
}

int
main (int   argc,
      char *argv[])
{
  GtkWidget *window;
  GtkWidget *frame;
  GtkWidget *da;

  gtk_init (&amp;argc, &amp;argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");

  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);

  gtk_container_set_border_width (GTK_CONTAINER (window), 8);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (window), frame);

  da = gtk_drawing_area_new ();
  /* set a minimum size */
  gtk_widget_set_size_request (da, 100, 100);

  gtk_container_add (GTK_CONTAINER (frame), da);

  /* Signals used to handle the backing surface */
  g_signal_connect (da, "draw",
                    G_CALLBACK (draw_cb), NULL);
  g_signal_connect (da,"configure-event",
                    G_CALLBACK (configure_event_cb), NULL);

  /* Event signals */
  g_signal_connect (da, "motion-notify-event",
                    G_CALLBACK (motion_notify_event_cb), NULL);
  g_signal_connect (da, "button-press-event",
                    G_CALLBACK (button_press_event_cb), NULL);

  /* Ask to receive events the drawing area doesn't normally
   * subscribe to. In particular, we need to ask for the
   * button press and motion notify events that want to handle.
   */
  gtk_widget_set_events (da, gtk_widget_get_events (da)
                             | GDK_BUTTON_PRESS_MASK
                             | GDK_POINTER_MOTION_MASK
                             | GDK_POINTER_MOTION_HINT_MASK);

  gtk_widget_show_all (window);

  gtk_main ();

  return 0;
}

      </pre></div>
</div>
<br class="example-break">
</div>
<div class="simplesect">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="id3120339"></a>Building interfaces</h2></div></div></div>
<p>When construcing a more complicated user interface, with dozens
    or hundreds of widgets, doing all the setup work in C code is
    cumbersome, and making changes becomes next to impossible.</p>
<p>Thankfully, GTK+ supports the separation of user interface
    layout from your business logic, by using UI descriptions in an
    XML format that can be parsed by the #GtkBuilder class.</p>
<div class="example">
<a name="id3120358"></a><p class="title"><b>Example 4. Packing buttons with GtkBuilder</b></p>
<div class="example-contents">
<pre class="programlisting">
        <span style="color: red">&lt;include&gt;
          <span style="color: red">&lt;fallback&gt;FIXME: MISSING XINCLUDE CONTENT&lt;/fallback&gt;</span>
        &lt;/include&gt;</span>
      </pre>
      The builder.ui file looks like this:
      <pre class="programlisting">
        <span style="color: red">&lt;include&gt;
          <span style="color: red">&lt;fallback&gt;FIXME: MISSING XINCLUDE CONTENT&lt;/fallback&gt;</span>
        &lt;/include&gt;</span>
      </pre>
</div>
</div>
<br class="example-break"><p>Note that GtkBuilder can also be used to construct objects
    that are not widgets, such as tree models, adjustments, etc.
    That is the reason the method we use here is called
    gtk_builder_get_object() and returns a GObject* instead of a
    GtkWidget*.</p>
<p>Normally, you would pass a full path to
    gtk_builder_add_from_file() to make the execution of your program
    independent of the current directory. A common location to install
    UI descriptions and similar data is
    <code class="filename">/usr/share/<em class="replaceable"><code>appname</code></em></code>.
    </p>
<p>It is also possible to embed the UI description in the source
    code as a string and use gtk_builder_add_from_string() to load it.
    But keeping the UI description in a separate file has several
    advantages: It is then possible to make minor adjustments to the UI
    without recompiling your program, and, more importantly, graphical
    UI editors such as <a class="ulink" href="http://glade.gnome.org" target="_top">glade</a>
    can load the file and allow you to create and modify your UI by
    point-and-click.</p>
</div>
</div>
<div class="footer">
<hr>
          Generated by GTK-Doc V1.18</div>
</body>
</html>